﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using SlimDX.Direct3D10;


namespace SGX
{
    class Scene : IDisposable
    {
        Device m_device;
        SceneNode m_root;
        SlimDX.Direct3D9.MatrixStack m_matrixStack;

        System.Diagnostics.Stopwatch m_stopWatch;
        long m_totalElapsedTime;

        public EffectState m_GlobalEffectPool;

        //Temporary camera information. Later have to implement it through a sofisticated camera class
        Vector3 cameraPos;
        Vector3 lookAt;
        Vector3 up;
        float nearPlane, farPlane;
        float viewAngle, aspectRatio;
        Matrix view, projection;
        //temporary light
        Light[] light;

        public Scene(Device device, SceneNode root)
        {
            m_device = device;
            m_root = root;
            m_stopWatch = new System.Diagnostics.Stopwatch();
            m_stopWatch.Reset();
            m_totalElapsedTime = 0L;

            m_matrixStack = new SlimDX.Direct3D9.MatrixStack();
            
            m_GlobalEffectPool = new EffectState();
            m_GlobalEffectPool.LoadEffectPool("shaders\\sharedpool.hlsli");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Matrix, "gView");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Scalar, "gdt");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Matrix, "gProjection");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Scalar, "gLights");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Vector, "gEyePosW");
            m_GlobalEffectPool.AddShaderVariable(ShaderVariableType.Scalar, "gNumLights");

            //intialize camera and perpective view information
            cameraPos = new Vector3(30, 30, 30);
            lookAt = Vector3.Zero;
            up = Vector3.UnitY;
            nearPlane = 0.1f;
            farPlane = 1000.0f;
            viewAngle = (float)(Math.PI / 2.0);
            aspectRatio = 1.0f;

            view = Matrix.LookAtLH(cameraPos, lookAt, up);
            projection = Matrix.PerspectiveFovLH(viewAngle, aspectRatio, nearPlane, farPlane);

            light = new Light[1];
            light[0].ambient = new Vector4(1, 1, 1, 1);
            light[0].diffuse = new Vector4(1, 1, 1, 1);
            light[0].att = new Vector3(0, 1, 0);
            light[0].dir = new Vector3(-30, -20, -40);
            light[0].pos = Vector3.Zero;
            light[0].range = 10000.0f;
            light[0].spec = new Vector4(1, 1, 1, 1);
            //light.spotAtt = new Vector3
            light[0].type = LightType.Directed;

            m_GlobalEffectPool.SetMatrix("gView", view);
            m_GlobalEffectPool.SetMatrix("gProjection", projection);
            DataStream data = new DataStream(104, true, true);
            data.WriteRange(light, 0, 0);
            m_GlobalEffectPool.SetRawValue("gLights", data, 104);
            data.Dispose();

            m_GlobalEffectPool.Set("gEyePosW", cameraPos);
            m_GlobalEffectPool.Set("gNumLights", 1);
        }

        public SceneNode Root
        {
            set { m_root = value; }
            get { return m_root; }
        }

        public Device Device
        {
            get { return m_device; }
        }

        public void Dispose()
        {
            m_root.Dispose();
            m_matrixStack.Dispose();
        }

        public void Render()
        {
            if (m_root != null)
            {
                m_root.PreRender(this);
                m_root.Render(this);
                m_root.RenderChildren(this);
                m_root.PostRender(this);
            }
        }

        public void Update()
        {
            long elapsed = 0;
            if (m_stopWatch.IsRunning)
            {
                elapsed = m_stopWatch.ElapsedTicks - m_totalElapsedTime;
                float dt = (float)elapsed / (float)TimeSpan.TicksPerSecond;
                if (m_root != null)
                {
                    m_root.Update(this, dt);
                }
                m_totalElapsedTime += elapsed;
            }
            else
                m_stopWatch.Start();

            m_root.Update(this, elapsed);
            m_GlobalEffectPool.Set("gdt", elapsed);
        }

        public void OnResize(int width, int height)
        {

        }
        public SlimDX.Direct3D9.MatrixStack MatrixStack
        {
            get { return m_matrixStack; }
        }
    }
}
